package info.motteke.qjdbc.exec;

import static info.motteke.qjdbc.dto.DtoMetaInnerAPI.*;
import info.motteke.qjdbc.Statement;
import info.motteke.qjdbc.column.Meta;
import info.motteke.qjdbc.column.variable.Variable;
import info.motteke.qjdbc.column.variable.VariableInnerAPI;
import info.motteke.qjdbc.dto.DtoMeta;
import info.motteke.qjdbc.exception.SqlException;
import info.motteke.qjdbc.exception.TooManyEntitiesException;
import info.motteke.util.Closer;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class ResultSetStatement<T> implements Statement {

    private final PreparedStatement pstmt;

    private final DtoMeta<T> meta;

    private final Set<Meta> columns;

    private final List<Variable> vars;

    public ResultSetStatement(PreparedStatement pstmt, DtoMeta<T> meta, Set<Meta> columns, List<Variable> vars){
        this.pstmt = pstmt;
        this.meta = meta;
        this.columns = columns;
        this.vars = vars;
    }

    public List<T> asList(){
        ResultSet rs = null;

        try{
            rs = execute();
            List<T> list = new ArrayList<T>();

            while(rs.next()){
                T obj = newInstance(meta);
                setValues(meta, rs, obj, columns);
                list.add(obj);
            }
            return list;
        } catch (SQLException e) {
            throw new SqlException(e);
        } finally {
            Closer.close(rs);
            Closer.close(pstmt);
        }
    }

    public T asSingle() {
        ResultSet rs = null;

        try {
            rs = execute();

            if(!rs.next()){
                return null;
            }
            T obj = newInstance(meta);
            setValues(meta, rs, obj, columns);

            if(rs.next()){
                throw new TooManyEntitiesException();
            }else{
                return obj;
            }
        } catch (SQLException e){
            throw new SqlException(e);
        } finally {
            Closer.close(rs);
            Closer.close(pstmt);
        }
    }

    private ResultSet execute() throws SQLException{
        for(int i = 1, length = vars.size(); i < length; i++){
            Variable var = vars.get(i);
            VariableInnerAPI.putValue(var, pstmt, i);
        }
        return pstmt.executeQuery();
    }
}
